package cn.melonlib.cores.commons.comp.dao;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.melonlib.cores.commons.model.entity.IEntity;
import cn.melonlib.objects.maps.JsonMap;
import com.google.common.base.CaseFormat;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.stream.Collectors;

import static cn.melonlib.cores.commons.comp.dao.ConditionTrans.*;

@Component
@Lazy(false)
public class DaoUtils {

    public JsonMap<ConditionTrans> trans;

    @PostConstruct
    public void init() {
        trans = new JsonMap<ConditionTrans>()
                .append(EQ, (name, idx, value) -> new StringBuilder(name.endsWith("_id")?name.replaceAll("_id$",".id"):name).append("=?").append(idx).toString())
                .append(NEQ, (name, idx, value) -> new StringBuilder(name.endsWith("_id")?name.replaceAll("_id$",".id"):name).append("!=?").append(idx).toString())
                .append(LIKE, (name, idx, value) -> new StringBuilder(name.endsWith("_id")?name.replaceAll("_id$",".id"):name).append(" like ?").append(idx).toString())
                .append(GT, (name, idx, value) -> new StringBuilder(name.endsWith("_id")?name.replaceAll("_id$",".id"):name).append(" > ?").append(idx).toString())
                .append(LT, (name, idx, value) -> new StringBuilder(name.endsWith("_id")?name.replaceAll("_id$",".id"):name).append(" < ?").append(idx).toString())
                .append(GTE, (name, idx, value) -> new StringBuilder(name.endsWith("_id")?name.replaceAll("_id$",".id"):name).append(" >= ?").append(idx).toString())
                .append(LTE, (name, idx, value) -> new StringBuilder(name.endsWith("_id")?name.replaceAll("_id$",".id"):name).append(" <= ?").append(idx).toString())
                .append(IN, (name, idx, value) -> {
                    if (ObjectUtils.isNotEmpty(value)) {
                        StringBuilder condition = new StringBuilder(name.endsWith("_id")?name.replaceAll("_id$",".id"):name).append(" in(");
                        if (value instanceof Collection) {
                            List<Object> values = (List<Object>) value;
                            for (int start = idx; start < idx + values.size(); start++) {
                                if (start != idx) {
                                    condition.append(",");
                                }
                                condition.append("?").append(start);
                            }
                        } else {
                            condition.append("?").append(idx);
                        }
                        return condition.append(")").toString();
                    } else {
                        return "";
                    }

                })
                .append(NIN, (name, idx, value) -> {
                    if (ObjectUtils.isNotEmpty(value)) {
                        StringBuilder condition = new StringBuilder(name.endsWith("_id")?name.replaceAll("_id$",".id"):name).append(" not in(");
                        if (value instanceof Collection) {
                            List<Object> values = (List<Object>) value;
                            for (int start = idx; start < idx + values.size(); start++) {
                                if (start != idx) {
                                    condition.append(",");
                                }
                                condition.append("?").append(start);
                            }
                        } else {
                            condition.append("?").append(idx);
                        }
                        return condition.append(")").toString();
                    } else {
                        return "";
                    }
                })
        ;
    }

    public String filterToQl(JsonMap<? extends Object> filter) {
        return filterToQl(filter, 1);
    }

    public String filterToQl(JsonMap<? extends Object> filter, int start) {
        StringBuilder ql = new StringBuilder();
        ArrayList<Integer> position = new ArrayList<>(1);
        position.add(start);
        Set<? extends Map.Entry<String, ?>> entrySet = filter.entrySet();
        for (Map.Entry<String, ? extends Object> entry : entrySet) {
            Map<String, Object> names = (Map<String, Object>) entry.getValue();
            if (position.get(0).intValue() != start) {
                ql.append(" and ");
            }
            String conditionItems = "(" + names.entrySet().stream().map(e -> {
                Object val = e.getValue();
                int size = 1;
                if (val instanceof Collection) {
                    size = ((Collection<?>) val).size();
                }
                int idx = position.get(0);
                position.set(0, idx + size);
                return trans.get(e.getKey()).trans(entry.getKey(), idx, val);
            }).collect(Collectors.joining(" and ")) + ")";
            ql.append(conditionItems);
        }
        return ql.toString();
    }

    public String filterToSql(JsonMap<? extends Object> filter, int start) {
        StringBuilder ql = new StringBuilder();
        ArrayList<Integer> position = new ArrayList<>(1);
        position.add(start);
        Set<? extends Map.Entry<String, ?>> entrySet = filter.entrySet();
        for (Map.Entry<String,? extends Object> entry : entrySet) {
            String name= CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE,entry.getKey());
            Map<String, Object> names = (Map<String, Object>) entry.getValue();
            if (position.get(0).intValue() != start) {
                ql.append(" and ");
            }
            String conditionItems = "(" + names.entrySet().stream().map(e -> {
                Object val = e.getValue();
                int size = 1;
                if (val instanceof Collection) {
                    size = ((Collection<?>) val).size();
                }
                int idx = position.get(0);
                position.set(0, idx + size);
                return trans.get(e.getKey()).trans(name, idx, val);
            }).collect(Collectors.joining(" and ")) + ")";
            ql.append(conditionItems);
        }
        return ql.toString();
    }

    public List<Object> filterParams(Class<? extends IEntity<? extends Serializable>> type, JsonMap<? extends Object> filter) {
        return filter.entrySet().stream().flatMap(mode -> {
            Map<String, Object> m = (Map<String, Object>) mode.getValue();
            return m.entrySet().stream().flatMap(entry->{
                if (entry.getValue() instanceof Collection) {
                    return ((Collection<?>) entry.getValue()).stream();
                } else if (entry.getValue() instanceof Number) {
                    List<PropertyDescriptor> pds = Arrays.stream(ReflectUtils.getBeanGetters(type))
                            .filter(getter -> Objects.equals(getter.getName(), mode.getKey())).collect(Collectors.toList());
                    if (pds.isEmpty()) {
                        return List.of(entry.getValue()).stream();
                    } else {
                        PropertyDescriptor pd = pds.get(0);
                        if (Objects.equals(pd.getPropertyType(), Double.class)) {
                            return List.of(((Number) entry.getValue()).doubleValue()).stream();
                        } else if (Objects.equals(pd.getPropertyType(), Long.class)) {
                            return List.of(((Number) entry.getValue()).longValue()).stream();
                        } else if (Objects.equals(pd.getPropertyType(), Integer.class)) {
                            return List.of(((Number) entry.getValue()).intValue()).stream();
                        } else if (Objects.equals(pd.getPropertyType(), Float.class)) {
                            return List.of(((Number) entry.getValue()).floatValue()).stream();
                        } else {
                            return List.of(entry.getValue().toString()).stream();
                        }
                    }
                } else {
                    if (Objects.equals("like", entry.getKey())) {
                        return List.of("%" + entry.getValue() + "%").stream();
                    }
                    return List.of(entry.getValue()).stream();
                }
            });
        }).collect(Collectors.toList());
    }
    public <E>List<E> filterParams(JsonMap<? extends Object> filter) {
        return filter.entrySet().stream().flatMap(mode -> {
            Map<String, Object> m = (Map<String, Object>) mode.getValue();
            return m.entrySet().stream().flatMap(entry->{
                if (entry.getValue() instanceof Collection) {
                    return ((Collection<?>) entry.getValue()).stream();
                } else if (entry.getValue() instanceof Number) {
                    return List.of(entry.getValue().toString()).stream();
                } else {
                    if (Objects.equals("like", entry.getKey())) {
                        return List.of("%" + entry.getValue() + "%").stream();
                    }
                    return List.of(entry.getValue()).stream();
                }
            });
        }).map(e->(E)e).collect(Collectors.toList());
    }

    public JsonMap<JsonMap<Object>> filterBeanToMap(Object bean){
        Arrays.stream(BeanUtil.getPropertyDescriptors(bean.getClass())).reduce(JsonMap.createMap(),(m,p)->{
            System.out.println(p.getName());
            try {
                Object obj=p.getReadMethod().invoke(bean);
                if(Objects.nonNull(obj)){
                    Arrays.stream(BeanUtil.getPropertyDescriptors(obj.getClass())).reduce(JsonMap.createMap(),(m2,p2)->{
                        try {
                            Object value = p.getReadMethod().invoke(obj);
                        }catch (Exception e){
                            return m2;
                        }
                        return m;
                    },(m2,p2)->null);
                }
            } catch (Exception e) {
                return m;
            }
            return m;
        },(m,P)->null);
        return null;
    }
}
